﻿#region Header File
//
// ByteChannel.cs - Discrete channel (byte-oriented) with errors
//
// Copyright (C) Javier Valcarce. BSD License
#endregion

#region Using Statements
using System;
using System.Threading;
using System.IO;
using System.IO.Ports;
#endregion


public class ByteChannel : IDisposable
{
    Random rnd1;
    Random rnd2;

    Thread th1;
    double pe;

    public Stream I     { get; private set; }
    public Stream O     { get; private set; }

    /// <summary>Number of bytes relayed</summary>
    public long ByteCount  { get; private set; }
    /// <summary>Number of errors introduced</summary>
    public long ErrorCount { get; private set; }


    /// <summary>
    /// </summary>
    /// <param name="pe">Probability of error</param>
    /// <param name="istream">Input  stream</param>
    /// <param name="ostream">Output stream</param>
    public ByteChannel(double pe, Stream istream, Stream ostream)
    {
        this.pe = pe;
        I = istream;
        O = ostream;
        ByteCount  = 0;
        ErrorCount = 0;

        if (pe < 0.0)    throw new ArgumentException();
        if (pe > 1.0)    throw new ArgumentException();
        if (!I.CanRead ) throw new ArgumentException();
        if (!O.CanWrite) throw new ArgumentException();

        rnd1 = new Random();
        rnd2 = new Random();
        th1  = new Thread(Relayer);

        th1.IsBackground = true;
        th1.Start();
    }

    // Ends the relayer thread
    public void Dispose()
    {
        th1.Interrupt();
    }


    public void Relayer()
	{
        byte[] b = new byte[1];
        byte   n;
        double s;

        while (true)
        {
            try
            {
                // compute next error sample
                s = rnd1.NextDouble();
                if (s < pe)
                {
                    // uniformly distributed bit errors 
                    // error 0x00 represents no error and is not valid
                    n = (byte) (1 + 254 * rnd2.NextDouble());
                    ErrorCount++;
                }
                else
                {
                    n = 0;
                }

                I.Read (b, 0, 1);

                // simulate transmission error 
                b[0] ^= n;

                O.Write(b, 0, 1);
                ByteCount++;
            }
            catch (ThreadInterruptedException )
            {
                // 
                break;
            }
        }
	}
}
